#include "common.h"
//#include "xaglog/xaglog.hpp"
//#include "linux/linux_util.h"
#include <sys/sysinfo.h>

#if 0
namespace {
#undef SPDLOG_TRACE
#undef SPDLOG_DEBUG
#undef SPDLOG_INFO
#undef SPDLOG_WARN
#undef SPDLOG_ERROR
#undef SPDLOG_CRITICAL
constexpr char xaglogger[] = "DEAMON";
#define SPDLOG_TRACE(...) XAGLOG_TOPIC_TRACE(xaglogger, ##__VA_ARGS__)
#define SPDLOG_DEBUG(...) XAGLOG_TOPIC_DEBUG(xaglogger, ##__VA_ARGS__)
#define SPDLOG_INFO(...) XAGLOG_TOPIC_INFO(xaglogger, ##__VA_ARGS__)
#define SPDLOG_WARN(...) XAGLOG_TOPIC_WARN(xaglogger, ##__VA_ARGS__)
#define SPDLOG_ERROR(...) XAGLOG_TOPIC_ERROR(xaglogger, ##__VA_ARGS__)
#define SPDLOG_CRITICAL(...) XAGLOG_TOPIC_CRITICAL(xaglogger, ##__VA_ARGS__)
} 
#endif

namespace xag {
namespace xos {
namespace deamon {

void Common::read_uptime(uint64_t *uptime)
{
  FILE *fp = NULL;
  char line[128];
  unsigned long up_sec, up_cent;
  bool err = false;

  if ((fp = fopen(UPTIME, "r")) == NULL) {
    err = true;
  }
  else if (fgets(line, sizeof(line), fp) == NULL) {
    err = true;
  }
  else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
    *uptime = (uint64_t) up_sec * 100 +
        (uint64_t) up_cent;
  }
  else {
    err = true;
  }

  if (fp != NULL) {
    fclose(fp);
  }

  if (err) {
    fprintf(stderr, "Cannot read %s\n", UPTIME);
    exit(2);
  }
}

void Common::get_kb_shift(void)
{
  int shift = 0;
  long size;

  /* One can also use getpagesize() to get the size of a page */
  if ((size = sysconf(_SC_PAGESIZE)) == -1) {
    perror("sysconf");
  }

  page_size = size;

  size >>= 10;  /* Assume that a page has a minimum size of 1 kB */

  while (size > 1) {
    shift++;
    size >>= 1;
  }

  kb_shift = (uint32_t) shift;
}

void Common::get_HZ(void)
{
  long ticks;

  if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
    perror("sysconf");
  }

  //SPDLOG_INFO("ticks {}",ticks);
  printf("ticks = %ld",ticks);

  Common::SYS_HZ = (unsigned long) ticks;

  //SPDLOG_INFO("Common::SYS_HZ {}",Common::SYS_HZ);
  printf("SYS_HZ = %ld\n", Common::SYS_HZ);
}

void Common::get_cpu_num(void)
{
  sys_cpu_num = get_nprocs_conf();
  //SPDLOG_INFO("sys_cpu_num {}",sys_cpu_num);
  printf("sys_cpu_num = %d", sys_cpu_num);
}

uint64_t Common::get_interval(uint64_t prev_uptime,
                              uint64_t curr_uptime)
{
  uint64_t itv;

  /* prev_time=0 when displaying stats since system startup */
  itv = curr_uptime - prev_uptime;

  if (!itv) {  /* Paranoia checking */
    itv = 1;
  }

  return itv;
}

void Common::add(pid_stats* a,pid_stats* b)
{
  a->nvcsw += b->nvcsw;
  a->nivcsw += b->nivcsw;
  a->read_bytes += b->read_bytes;
  a->write_bytes += b->write_bytes;
  a->cancelled_write_bytes += b->cancelled_write_bytes; 
  a->threads = b->threads;
}

void Common::get_st_pid(st_pid* a,const st_pid* b)
{
  a->pid = b->pid;
  a->uid = b->uid;
  a->tgid = b->tgid;
  a->name = b->name;

  for (int i = 0;i < 2;i++) {
    a->pstats[i]->read_bytes = b->pstats[i]->read_bytes;
    a->pstats[i]->write_bytes = b->pstats[i]->write_bytes;
    a->pstats[i]->cancelled_write_bytes = b->pstats[i]->cancelled_write_bytes;
    a->pstats[i]->utime = b->pstats[i]->utime;
    a->pstats[i]->stime = b->pstats[i]->stime;
    a->pstats[i]->gtime = b->pstats[i]->gtime;
    a->pstats[i]->wtime = b->pstats[i]->wtime;
    a->pstats[i]->nvcsw = b->pstats[i]->nvcsw;
    a->pstats[i]->nivcsw = b->pstats[i]->nivcsw;
    a->pstats[i]->threads = b->pstats[i]->threads;
    a->pstats[i]->fd_nr = b->pstats[i]->fd_nr;
  }
}

void Common::get_st_pid(st_pid* a,int32_t a_idx,const st_pid* b,int32_t b_idx)
{
  a->pid = b->pid;
  a->uid = b->uid;
  a->tgid = b->tgid;
  a->name = b->name;

  a->pstats[a_idx]->read_bytes = b->pstats[b_idx]->read_bytes;
  a->pstats[a_idx]->write_bytes = b->pstats[b_idx]->write_bytes;
  a->pstats[a_idx]->cancelled_write_bytes = b->pstats[b_idx]->cancelled_write_bytes;
  a->pstats[a_idx]->utime = b->pstats[b_idx]->utime;
  a->pstats[a_idx]->stime = b->pstats[b_idx]->stime;
  a->pstats[a_idx]->gtime = b->pstats[b_idx]->gtime;
  a->pstats[a_idx]->wtime = b->pstats[b_idx]->wtime;
  a->pstats[a_idx]->nvcsw = b->pstats[b_idx]->nvcsw;
  a->pstats[a_idx]->nivcsw = b->pstats[b_idx]->nivcsw;
  a->pstats[a_idx]->threads = b->pstats[b_idx]->threads;
  a->pstats[a_idx]->fd_nr = b->pstats[b_idx]->fd_nr;
}

void Common::display_info(struct st_pid *plist,int prev, int curr,uint64_t itv)
{
  pid_stats *pstc, *pstp;
  if (plist == nullptr) {
    perror("get null");
    return;
  }

  pstc = plist->pstats[curr].get();
  pstp = plist->pstats[prev].get();

  double rbytes = S_VALUE(pstp->read_bytes,  pstc->read_bytes, itv) / 1024;
  double wbytes = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv) / 1024;
  double cbytes = S_VALUE(pstp->cancelled_write_bytes,pstc->cancelled_write_bytes, itv)  / 1024;

  #if 0
  SPDLOG_INFO("itv {} {} {}",itv ,pstp->stime,pstc->stime);

  SPDLOG_INFO("{:^} {:^#02f} {:^#02f} {:^#02f} {:^#02f} {:^} {:^#02f} {:^#02f} {:^#02f} {:^#02f} {:^#02f} {:^}", 
                  plist->pid,
                  (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ?
                  0.0 :
                  SP_VALUE(pstp->utime - pstp->gtime,pstc->utime - pstc->gtime, itv * Common::SYS_HZ / 100),
                  SP_VALUE(pstp->stime,  pstc->stime, itv * Common::SYS_HZ / 100),
                  SP_VALUE(pstp->gtime,  pstc->gtime, itv * Common::SYS_HZ / 100),
                  //SP_VALUE(pstp->wtime,  pstc->wtime, itv * Common::SYS_HZ / 100),
                  SP_VALUE(pstp->utime + pstp->stime,pstc->utime + pstc->stime, itv * Common::SYS_HZ / 100),
                  pstp->threads,
                  rbytes,wbytes,cbytes,
                  S_VALUE(pstp->nvcsw, pstc->nvcsw, itv),
                  S_VALUE(pstp->nivcsw, pstc->nivcsw, itv),
                  plist->name);
  #endif
}

MemoryInformation GetProgressMemory(const std::string &pid) 
{
  MemoryInformation mem_info{};
  std::string process_name_address = "/proc/" + pid + "/statm";
  FILE *file_fd = NULL;
  char buf[1024]{0};

  file_fd = ::fopen(process_name_address.c_str(), "r");
  if (file_fd == NULL) {
    ::perror("fopen GetProgressMemory error");
    return mem_info;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf,"%u %u %u",&mem_info.total_memory, &mem_info.physical_memory, &mem_info.shared_memory);

  mem_info.total_memory = mem_info.total_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
  mem_info.physical_memory = mem_info.physical_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
  mem_info.shared_memory = mem_info.shared_memory * xag::xos::deamon::Common::page_size / 1024 / 1024;
  ::fclose(file_fd);

  return mem_info;
}

ProgramState Common::get_program_state(st_pid *plist,
                                         int prev, 
                                         int curr,
                                         uint64_t itv)
{
  ProgramState program_state;
  pid_stats *pstc, *pstp;
  if (plist == nullptr) {
    perror("get null");
    return program_state;
  }

  pstc = plist->pstats[curr].get();
  pstp = plist->pstats[prev].get();

  auto mem_info = GetProgressMemory(std::to_string(plist->pid));
  program_state.total_memory    = mem_info.total_memory; 
  program_state.physical_memory = mem_info.physical_memory;
  program_state.shared_memory   = mem_info.shared_memory;

  program_state.name  = plist->name;         
  program_state.pid   = plist->pid;                
  program_state.cpu   =  SP_VALUE(pstp->utime + pstp->stime,pstc->utime + pstc->stime, itv * Common::SYS_HZ / 100)/Common::sys_cpu_num;               
  program_state.user_cpu = (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ?
                            0.0 :
                            SP_VALUE(pstp->utime - pstp->gtime,pstc->utime - pstc->gtime, itv * Common::SYS_HZ / 100)/Common::sys_cpu_num;          
  program_state.system_cpu = SP_VALUE(pstp->stime,  pstc->stime, itv * Common::SYS_HZ / 100)/Common::sys_cpu_num;        
  program_state.thread_num = pstp->threads;
  program_state.nvcsw   = S_VALUE(pstp->nvcsw, pstc->nvcsw, itv);           
  program_state.nivcsw  = S_VALUE(pstp->nivcsw, pstc->nivcsw, itv);   
  // program_state.rbytes  = S_VALUE(pstp->read_bytes,  pstc->read_bytes, itv) / 1024;
  // program_state.wbytes  = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv) / 1024;
  // program_state.cbytes  = S_VALUE(pstp->cancelled_write_bytes,pstc->cancelled_write_bytes, itv)  / 1024;      
  
  return program_state;
}

}
}
}